package com.pubinfo.passbook.common.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class XmlTool {
  
  private static final XmlMapper xmlMapper = new XmlMapper();
  private static final ObjectMapper objectMapper = new ObjectMapper();
  
  @SneakyThrows
  public static JSONObject xml2json(String xml) {
    JSONObject jsonObject = new JSONObject();
    if (StringUtils.isEmpty(xml)) {
      return null;
    }
    SAXReader reader = new SAXReader();
    reader.setEncoding(StandardCharsets.UTF_8.name());
    Document document = null;
  
    document = reader.read(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
  
    Element node = document.getRootElement();
    
    iterateNodes(node, jsonObject);
    return jsonObject;
  }
  
  /**
   * 使用jackson实现json转xml
   */
  public static String xml2jsonByJackson(String xml) {
    StringWriter w = new StringWriter();
    try {
      JsonParser jp = xmlMapper.getFactory().createParser(xml);
      JsonGenerator jg = objectMapper.getFactory().createGenerator(w);
      while (jp.nextToken() != null) {
        jg.copyCurrentEvent(jp);
      }
      jp.close();
      jg.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return w.toString();
  }
  
  public static boolean isXmlDocument(String rtnMsg) {
    boolean flag = true;
    try {
      DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
      builder.parse(new InputSource(new StringReader(rtnMsg)));
    } catch (Exception e) {
      flag = false;
    }
    return flag;
  }
  
  /**
   * 利用StringBuffer将JSONObject拼接成xml
   */
  private static void json2xml(JSONObject jObj, StringBuffer buffer) {
    Set<Map.Entry<String, Object>> se = jObj.entrySet();
    for (Map.Entry<String, Object> en : se) {
      if ("com.alibaba.fastjson.JSONObject".equals(en.getValue().getClass().getName())) {
        buffer.append("<").append(en.getKey()).append(">");
        JSONObject jo = jObj.getJSONObject(en.getKey());
        json2xml(jo, buffer);
        buffer.append("</").append(en.getKey()).append(">");
      } else if ("com.alibaba.fastjson.JSONArray".equals(en.getValue().getClass().getName())) {
        JSONArray jarray = jObj.getJSONArray(en.getKey());
        for (int i = 0; i < jarray.size(); i++) {
          buffer.append("<").append(en.getKey()).append(">");
          JSONObject jsonobject = jarray.getJSONObject(i);
          json2xml(jsonobject, buffer);
          buffer.append("</").append(en.getKey()).append(">");
        }
      } else if ("java.lang.String".equals(en.getValue().getClass().getName())) {
        buffer.append("<").append(en.getKey()).append(">").append(en.getValue());
        buffer.append("</").append(en.getKey()).append(">");
      }
    }
  }
  
  /**
   * 遍历元素
   *
   * @param node
   * @param json
   */
  private static void iterateNodes(Element node, JSONObject json) {
    //获取当前元素名称
    String nodeName = node.getName();
    //判断已遍历的JSON中是否已经有了该元素的名称
    if (json.containsKey(nodeName)) {
      //该元素在同级下有多个
      Object Object = json.get(nodeName);
      JSONArray array = null;
      if (Object instanceof JSONArray) {
        array = (JSONArray) Object;
      } else {
        array = new JSONArray();
        array.add(Object);
      }
      //获取该元素下所有子元素
      List<Element> listElement = node.elements();
      if (listElement.isEmpty()) {
        //该元素无子元素，获取元素的值
        String nodeValue = node.getTextTrim();
        array.add(nodeValue);
        json.put(nodeName, array);
        return;
      }
      //有子元素
      JSONObject newJson = new JSONObject();
      //遍历所有子元素
      for (Element e : listElement) {
        //递归
        iterateNodes(e, newJson);
      }
      array.add(newJson);
      json.put(nodeName, array);
      return;
    }
    //该元素同级下第一次遍历
    //获取该元素下所有子元素
    List<Element> listElement = node.elements();
    if (listElement.isEmpty()) {
      //该元素无子元素，获取元素的值
      String nodeValue = node.getTextTrim();
      json.put(nodeName, nodeValue);
      return;
    }
    //有子节点，新建一个JSONObject来存储该节点下子节点的值
    JSONObject object = new JSONObject();
    for (Element e : listElement) {
      //递归
      iterateNodes(e, object);
    }
    json.put(nodeName, object);
    return;
  }
  
  @SuppressWarnings({"unchecked"})
  public static <T> T convertToJavaBean(String xml, Class<T> t) throws Exception {
    T obj = null;
    JAXBContext context = JAXBContext.newInstance(t);
    Unmarshaller unmarshaller = context.createUnmarshaller();
    obj = (T) unmarshaller.unmarshal(new StringReader(xml));
    return obj;
  }
  
}